#pragma semicolon 1
#pragma newdecls required

#define PLUGIN_VERSION "1.2"
#include <sourcemod>
#include <sdktools>

ConVar h_SmokersSpeed;
float f_SmokersSpeed;
Handle RangeCheckTimer[MAXPLAYERS+1];
bool Grabbed[MAXPLAYERS+1];
ConVar h_TongueMaxStretch;
int TongueMaxStretch;

public Plugin myinfo = 
{
	name = "l4d2_smoke_n_move",
	author = "Olj",
	description = "smoker拉人可以移动.",
	version = PLUGIN_VERSION,
	url = "https://forums.alliedmods.net/showthread.php?p=936834"
}


public void OnPluginStart()
{
	CreateConVar("l4d_smokeit_version", PLUGIN_VERSION, "Version of Smoke It plugin", FCVAR_NOTIFY);
	h_SmokersSpeed = CreateConVar("l4d_smokeit_speed", "0.42", "Smoker's speed modifier", FCVAR_NOTIFY);
	h_TongueMaxStretch = CreateConVar("l4d_smokeit_tongue_stretch", "950", "Smoker's max tongue stretch (tongue will be released if beyond this)", FCVAR_NOTIFY);
    
	f_SmokersSpeed = h_SmokersSpeed.FloatValue;
	TongueMaxStretch = h_TongueMaxStretch.IntValue;
    
	h_SmokersSpeed.AddChangeHook(SmokersSpeedChanged);
	h_TongueMaxStretch.AddChangeHook(TongueMaxStretchChanged);
    
	HookEvent("tongue_grab", GrabEvent, EventHookMode_Pre);
	HookEvent("tongue_release", ReleaseEvent, EventHookMode_Pre);
}

public void SmokersSpeedChanged(ConVar convar, const char[] oldValue, const char[] newValue)
{
    f_SmokersSpeed = h_SmokersSpeed.FloatValue;
}            

public void TongueMaxStretchChanged(ConVar convar, const char[] oldValue, const char[] newValue)
{
    TongueMaxStretch = h_TongueMaxStretch.IntValue;
}            
    
public Action GrabEvent(Event event, const char[] name, bool dontBroadcast)
{
	int Smoker = GetClientOfUserId(event.GetInt("userid"));
	if (IsFakeClient(Smoker)) return Plugin_Continue;
    
	int Victim = GetClientOfUserId(event.GetInt("victim"));
	Grabbed[Smoker] = true;
    
	SetEntityMoveType(Smoker, MOVETYPE_ISOMETRIC);
	SetEntDataFloat(Smoker, FindSendPropInfo("CTerrorPlayer", "m_flLaggedMovementValue"), f_SmokersSpeed, true);
    
	DataPack pack;
	RangeCheckTimer[Smoker] = CreateDataTimer(0.2, RangeCheckTimerFunction, pack, TIMER_FLAG_NO_MAPCHANGE|TIMER_REPEAT);
	pack.WriteCell(Smoker);
	pack.WriteCell(Victim);
    
	return Plugin_Continue;
}

public Action RangeCheckTimerFunction(Handle timer, DataPack pack)
{
	pack.Reset();
	int Smoker = pack.ReadCell();
    
	if (!IsValidClient(Smoker) || GetClientTeam(Smoker) != 3 || IsFakeClient(Smoker) || !Grabbed[Smoker])
	{
		RangeCheckTimer[Smoker] = null;
		return Plugin_Stop;
	}
    
	int Victim = pack.ReadCell();
	if (!IsValidClient(Victim) || GetClientTeam(Victim) != 2 || !Grabbed[Smoker])
	{
		RangeCheckTimer[Smoker] = null;
		return Plugin_Stop;
	}
    
	float SmokerPosition[3];
	float VictimPosition[3];
	GetClientAbsOrigin(Smoker, SmokerPosition);
	GetClientAbsOrigin(Victim, VictimPosition);
    
	int distance = RoundToNearest(GetVectorDistance(SmokerPosition, VictimPosition));
    
	if (distance > TongueMaxStretch)
	{
		SlapPlayer(Smoker, 0, false);
	}
    
	return Plugin_Continue;
}

public Action ReleaseEvent(Event event, const char[] name, bool dontBroadcast)
{
	int Smoker = GetClientOfUserId(event.GetInt("userid"));
	Grabbed[Smoker] = false;
    
	SetEntityMoveType(Smoker, MOVETYPE_CUSTOM);
	SetEntDataFloat(Smoker, FindSendPropInfo("CTerrorPlayer", "m_flLaggedMovementValue"), 1.0, true);
    
	if (RangeCheckTimer[Smoker] != null)
	{
		delete RangeCheckTimer[Smoker];
		RangeCheckTimer[Smoker] = null;
	}
    
	return Plugin_Continue;
}

bool IsValidClient(int client)
{
	if (client <= 0 || client > MaxClients)
	return false;
    
	if (!IsClientConnected(client))
	return false;
    
	if (!IsClientInGame(client))
	return false;
    
	if (!IsPlayerAlive(client))
	return false;
        
	return true;
}